package com.zaizai.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.zaizai.constant.CommonConstant;
import com.zaizai.domain.Message;
import com.zaizai.domain.Role;
import com.zaizai.service.IChatSessionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 */
@Slf4j
@Service
public class ChatSessionServiceImpl implements IChatSessionService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 根据用户ID查找用户信息
     *
     * @param id 用户ID
     * @return 用户对象，如果不存在则返回null
     */
    @Override
    /**
     * 根据ID查找用户角色信息
     * 该方法首先检查传入的ID是否为null，然后根据ID的前缀从Redis中获取用户信息，
     * 如果信息存在，则将其解析为User对象并返回
     *
     * @param id 用户ID，可能带有特定前缀
     * @return 如果找到对应的用户信息，则返回Role对象；否则返回null
     */
    public Role findById(String id) {
        // 检查传入的ID是否为null
        if (id != null) {
            String value = null;
            // 判断ID是否以特定前缀开头，以确定在Redis中的键名
            if (id.startsWith(CommonConstant.USER_PREFIX)) {
                value = redisTemplate.boundValueOps(id).get();
            } else {
                value = redisTemplate.boundValueOps(CommonConstant.USER_PREFIX + id).get();
            }
            // 将获取到的字符串值解析为JSONObject
            JSONObject object = JSONObject.parseObject(value);
            // 如果解析成功且对象不为null，则将其转换为User类对象并返回
            if (object != null) {
                return object.toJavaObject(Role.class);
            }
        }
        // 如果传入的ID为null或没有找到对应的用户信息，则返回null
        return null;
    }

    /**
     * 推送消息给指定用户或群组
     *
     * @param fromId 发送方ID
     * @param toId   接收方ID，如果为null则表示群发
     * @param message 消息内容
     */
    @Override
    public void pushMessage(String fromId, String toId, String message) {
        // 创建消息实体
        Message entity = new Message();
        // 设置消息内容
        entity.setMessage(message);
        // 设置发送方信息
        entity.setFrom(this.findById(fromId));
        // 格式化当前时间，并将其设置为消息的创建时间
        entity.setCreateTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_PATTERN));
        if (toId != null) {
            // 查询接收方信息
            entity.setTo(this.findById(toId));
            // 单个用户推送
            push(entity, CommonConstant.CHAT_FROM_PREFIX + fromId + CommonConstant.CHAT_TO_PREFIX + toId);
        } else {
            // 公共消息 -- 群组
            entity.setTo(null);
            push(entity, CommonConstant.CHAT_COMMON_PREFIX + fromId);
        }
    }

    /**
     * 推送消息
     *
     * @param entity Session value
     * @param key    Session key
     */
    private void push(Message entity, String key) {
        //这里按照 PREFIX_ID 格式，作为KEY储存消息记录
        //但一个用户可能推送很多消息，VALUE应该是数组
        List<Message> list = new ArrayList<>();
        String value = redisTemplate.boundValueOps(key).get();
        if (value == null) {
            //第一次推送消息
            list.add(entity);
        } else {
            //第n次推送消息
            list = Objects.requireNonNull(JSONArray.parseArray(value, Message.class));
            list.add(entity);
        }
        redisTemplate.boundValueOps(key).set(JSONObject.toJSONString(list));
    }

    @Override
    /**
     * 获取在线用户的角色列表
     *
     * 本方法通过查询Redis中存储的在线用户键来获取在线用户的角色信息
     * 使用Redis的keys方法获取所有匹配的键，然后遍历这些键，调用findById方法获取每个键对应的角色对象
     *
     * @return 角色列表，包含所有在线用户的角色信息
     */
    public List<Role> onlineList() {
        // 初始化一个空的角色列表，用于存储在线用户的角色信息
        List<Role> list = new ArrayList<>();

        // 从Redis中获取所有符合用户前缀的角色键
        Set<String> keys = redisTemplate.keys(CommonConstant.USER_PREFIX + CommonConstant.REDIS_MATCH_PREFIX);

        // 如果存在匹配的键，则遍历这些键，获取每个键对应的角色信息
        if (keys != null && keys.size() > 0) {
            keys.forEach(key -> {
                // 调用findById方法根据键获取角色对象，并添加到角色列表中
                list.add(this.findById(key));
            });
        }

        // 返回包含所有在线用户角色信息的列表
        return list;
    }

    /**
     * 获取通用消息列表
     * 该方法从Redis中获取所有匹配特定前缀的键，然后从这些键中获取消息数据，
     * 将其解析并添加到一个列表中，最后返回按创建时间排序的消息列表
     *
     * @return List<Message> 返回按创建时间排序的消息列表
     */
    @Override
    public List<Message> commonList() {
        //初始化消息列表
        List<Message> list = new ArrayList<>();
        //获取Redis中所有匹配特定前缀的键
        Set<String> keys = redisTemplate.keys(CommonConstant.CHAT_COMMON_PREFIX + CommonConstant.REDIS_MATCH_PREFIX);
        //如果存在匹配的键，则遍历这些键以获取并解析消息数据
        if (keys != null && keys.size() > 0) {
            keys.forEach(key -> {
                //从Redis中获取消息数据的字符串表示
                String value = redisTemplate.boundValueOps(key).get();
                //将字符串解析为消息对象列表，并添加到结果列表中
                List<Message> messageList = Objects.requireNonNull(JSONArray.parseArray(value, Message.class));
                list.addAll(messageList);
            });
        }
        //对List集合中的数据按照时间顺序排序
        list.sort(Comparator.comparing(Message::getCreateTime));
        //返回排序后的消息列表
        return list;
    }

    /**
     * 获取个人消息列表
     *
     * @param fromId 发送方ID
     * @param toId   接收方ID
     * @return 个人消息列表,按发送时间排序
     */
    @Override
    public List<Message> selfList(String fromId, String toId) {
        List<Message> list = new ArrayList<>();

        // 从Redis中获取从A用户到B用户的消息列表
        String fromTo = redisTemplate.boundValueOps(CommonConstant.CHAT_FROM_PREFIX + fromId + CommonConstant.CHAT_TO_PREFIX + toId).get();
        // 从Redis中获取从B用户到A用户的消息列表
        String toFrom = redisTemplate.boundValueOps(CommonConstant.CHAT_FROM_PREFIX + toId + CommonConstant.CHAT_TO_PREFIX + fromId).get();

        // 如果从A到B的消息列表不为空，则解析并添加到返回列表中
        if (fromTo != null) {
            JSONArray fromToObject = JSONArray.parseArray(fromTo);
            list.addAll(fromToObject.toJavaList(Message.class));
        }
        // 如果从B到A的消息列表不为空，则解析并添加到返回列表中
        if (toFrom != null) {
            JSONArray toFromObject = JSONArray.parseArray(toFrom);
            list.addAll(toFromObject.toJavaList(Message.class));
        }

        // 如果列表中有消息，则按创建时间排序后返回；否则返回一个新的空列表
        if (list.size() > 0) {
            list.sort(Comparator.comparing(Message::getCreateTime));
            return list;
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * 从Redis中删除指定用户的信息
     *
     * @param id 用户ID
     */
    @Override
    public void delete(String id) {
        if (id != null) {
            log.info("从Redis中删除此Key: " + id);
            redisTemplate.delete(CommonConstant.USER_PREFIX + id);
        }
    }
}
